package cn.com.sinux.spring.utils;

import cn.com.sinux.spring.commons.Constants;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;

import static cn.com.sinux.spring.commons.Constants.POST;
import static cn.com.sinux.spring.commons.Constants.UTF8;

public class RequestUtils {

    private static final String contextPathName = "contextPath";
    /**
     * 默认关键字命名.
     */
    public static final String GZIP = "__gzip";


    public static String getContextPath(HttpServletRequest request) {
        String contextPath = (String) request.getAttribute(contextPathName);

        if (contextPath == null) {
            contextPath = request.getContextPath();
            if (contextPath != null && contextPath.length() > 0) {
                if ("/".equals(StringUtils.right(contextPath, 1))) {
                    contextPath = StringUtils.substringBeforeLast(contextPath, "/");
                }
            } else {
                contextPath = "";
            }
            request.setAttribute(contextPathName, contextPath);
        }

        return contextPath;
    }

    public static int[] getRange(String s) {
        int[] ranges = new int[2];
        ranges[0] = -1;
        ranges[1] = -1;

        s = StringUtils.trimToEmpty(s);
        s = StringUtils.removeStart(s, "bytes=");
        char[] cs = s.toCharArray();

        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        boolean first = true;
        if (cs != null && cs.length > 0) {
            for (int i = 0; i < cs.length; i++) {
                if ('-' == cs[i]) {
                    first = false;
                    continue;
                }

                if (first)
                    s1.append(cs[i]);
                else
                    s2.append(cs[i]);

            }
        }

        ranges[0] = NumberUtils.toInt(s1.toString(), -1);
        ranges[1] = NumberUtils.toInt(s2.toString(), -1);

        return ranges;
    }

    /**
     * 判断客户端浏览器是否支持GZip压缩.
     *
     * @param request HttpServletRequest.
     * @return 是否支持.
     */
    public static boolean isSupport(HttpServletRequest request) {

        // 手工设置不使用压缩
        String enableGzip = request.getParameter(GZIP);
        if ("false".equalsIgnoreCase(StringUtils.trimToEmpty(enableGzip))) {
            return false;
        }

        if (isIncluded(request)) {
            return false;
        }

        boolean support = false;
        String header = request.getHeader("Accept-Encoding");

        if (StringUtils.isNotBlank(header)) {
            header = header.toLowerCase(Locale.US);
            // 如果存在字符串gzip就表示支持
            support = header.indexOf("gzip") > -1;
        }

        return support;
    }

    /**
     * Checks if the request uri is an include.
     * These cannot be gzipped.
     *
     * @param request HttpServletRequest.
     * @return 是否是jsp include.
     */
    public static boolean isIncluded(final HttpServletRequest request) {
        final String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
        final boolean includeRequest = !(uri == null);

        if (includeRequest && LoggerFactory.getLogger(RequestUtils.class).isDebugEnabled()) {
            LoggerFactory.getLogger(RequestUtils.class).debug(request.getRequestURL() + " resulted in an include request. This is unusable, because"
                    + "the response will be assembled into the overrall response. Not gzipping.");
        }
        return includeRequest;
    }


    public static boolean isAjaxRequest(HttpServletRequest request) {
        String header = request.getHeader("X-Requested-With");
        return header != null && "XMLHttpRequest".equals(header);
    }

    /**
     * 获取真实请求路径
     *
     * @param url
     * @return
     */
    public static String getRequestUrl(String url) {
        if (StringUtils.isBlank(url)) {
            return url;
        }
        return StringUtils.substring(url, url.indexOf("/", 1), url.lastIndexOf("") + 3);
    }

    /**
     * 拼装当前路径.
     *
     * @param scheme      协议.
     * @param server      服务名称.
     * @param port        端口号.
     * @param contextPath 上下文路径.
     * @param servletPath Servlet path.
     * @param queryString Query String.
     * @return 完整的URL.
     */
    public static String getCurrentUrl(String scheme, String server, int port, String contextPath, String servletPath,
                                       String queryString) {
        StringBuffer url = new StringBuffer(128);
        //如果打开使用CCS应用配置的端口

        if (port < 0) {
            port = 80;
        }

        url.append(scheme).append("://").append(server);
        if ((scheme.equals("http") && (port != 80)) || (scheme.equals("https") && (port != 443))) {
            url.append(':').append(port);
        }

        url.append(contextPath).append(servletPath);

        if (!StringUtils.isBlank(queryString)) {
            url.append('?').append(queryString);
        }
        String returnUrl = "";
        try {
            returnUrl = URLEncoder.encode(url.toString(), "UTF-8");
        } catch (Exception e) {
            LoggerFactory.getLogger(RequestUtils.class).error(e.toString());
        }

        return returnUrl;
    }

    /**
     * 跳转到登录页面
     *
     * @param request
     * @param response
     * @return
     */
    public static String redirectLoginUrl(final String LoginUrl, HttpServletRequest request, HttpServletResponse response) {
        String url = LoginUrl;
        String currentUrl = getCurrentUrl(request.getScheme(), request.getServerName(),
                request.getServerPort(), request.getContextPath(), request.getServletPath(),
                request.getQueryString());

        LoggerFactory.getLogger(RequestUtils.class).debug("Get current path: {}", currentUrl);

        try {
            currentUrl = URLEncoder.encode(currentUrl, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            LoggerFactory.getLogger(RequestUtils.class).error(e.toString());
        }

        // 如果是本应用内的路径，则加上context path
        if (url.charAt(0) == '/') {
            String contextPath = request.getContextPath();

            if ("/".equals(contextPath)) {
                contextPath = StringUtils.EMPTY;
            }
            url = new StringBuilder(contextPath).append(url).append("?requestURL=").append(currentUrl).toString();
        }
        return url;
    }


    public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    private static final Logger log = LoggerFactory
            .getLogger(RequestUtils.class);

    /**
     * 获取QueryString的参数，并使用URLDecoder以UTF-8格式转码。如果请求是以post方法提交的，
     * 那么将通过HttpServletRequest#getParameter获取。
     *
     * @param request web请求
     * @param name    参数名称
     * @return
     */
    public static String getQueryParam(HttpServletRequest request, String name) {
        if (StringUtils.isBlank(name)) {
            return null;
        }
        if (request.getMethod().equalsIgnoreCase(POST)) {
            return request.getParameter(name);
        }
        String s = request.getQueryString();
        if (StringUtils.isBlank(s)) {
            return null;
        }
        try {
            s = URLDecoder.decode(s, UTF8);
        } catch (UnsupportedEncodingException e) {
            log.error("encoding " + UTF8 + " not support?", e);
        }
        String[] values = parseQueryString(s).get(name);
        if (values != null && values.length > 0) {
            return values[values.length - 1];
        } else {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public static Map<String, Object> getQueryParams(HttpServletRequest request) {
        Map<String, String[]> map;
        if (request.getMethod().equalsIgnoreCase(POST)) {
            map = request.getParameterMap();
        } else {
            String s = request.getQueryString();
            if (StringUtils.isBlank(s)) {
                return new HashMap<String, Object>();
            }
            try {
                s = URLDecoder.decode(s, UTF8);
            } catch (UnsupportedEncodingException e) {
                log.error("encoding " + UTF8 + " not support?", e);
            }
            map = parseQueryString(s);
        }

        Map<String, Object> params = new HashMap<String, Object>(map.size());
        int len;
        for (Map.Entry<String, String[]> entry : map.entrySet()) {
            len = entry.getValue().length;
            if (len == 1) {
                params.put(entry.getKey(), entry.getValue()[0]);
            } else if (len > 1) {
                params.put(entry.getKey(), entry.getValue());
            }
        }
        return params;
    }

    /**
     * Parses a query string passed from the client to the server and builds a
     * <code>HashTable</code> object with key-value pairs. The query string
     * should be in the form of a string packaged by the GET or POST method,
     * that is, it should have key-value pairs in the form <i>key=value</i>,
     * with each pair separated from the next by a &amp; character.
     * <p/>
     * <p/>
     * A key can appear more than once in the query string with different
     * values. However, the key appears only once in the hashtable, with its
     * value being an array of strings containing the multiple values sent by
     * the query string.
     * <p/>
     * <p/>
     * The keys and values in the hashtable are stored in their decoded form, so
     * any + characters are converted to spaces, and characters sent in
     * hexadecimal notation (like <i>%xx</i>) are converted to ASCII characters.
     *
     * @param s a string containing the query to be parsed
     * @return a <code>HashTable</code> object built from the parsed key-value
     * pairs
     * @throws IllegalArgumentException if the query string is invalid
     */
    public static Map<String, String[]> parseQueryString(String s) {
        String valArray[] = null;
        if (s == null) {
            throw new IllegalArgumentException();
        }
        Map<String, String[]> ht = new HashMap<String, String[]>();
        StringTokenizer st = new StringTokenizer(s, "&");
        while (st.hasMoreTokens()) {
            String pair = (String) st.nextToken();
            int pos = pair.indexOf('=');
            if (pos == -1) {
                continue;
            }
            String key = pair.substring(0, pos);
            String val = pair.substring(pos + 1, pair.length());
            if (ht.containsKey(key)) {
                String oldVals[] = (String[]) ht.get(key);
                valArray = new String[oldVals.length + 1];
                for (int i = 0; i < oldVals.length; i++) {
                    valArray[i] = oldVals[i];
                }
                valArray[oldVals.length] = val;
            } else {
                valArray = new String[1];
                valArray[0] = val;
            }
            ht.put(key, valArray);
        }
        return ht;
    }

    public static Map<String, String> getRequestMap(HttpServletRequest request,
                                                    String prefix) {
        return getRequestMap(request, prefix, false);
    }

    public static Map<String, String> getRequestMapWithPrefix(
            HttpServletRequest request, String prefix) {
        return getRequestMap(request, prefix, true);
    }

    @SuppressWarnings("unchecked")
    private static Map<String, String> getRequestMap(
            HttpServletRequest request, String prefix, boolean nameWithPrefix) {
        Map<String, String> map = new HashMap<String, String>();
        Enumeration<String> names = request.getParameterNames();
        String name, key, value;
        while (names.hasMoreElements()) {
            name = names.nextElement();
            if (name.startsWith(prefix)) {
                key = nameWithPrefix ? name : name.substring(prefix.length());
                value = StringUtils.join(request.getParameterValues(name), ',');
                map.put(key, value);
            }
        }
        return map;
    }

    /**
     * 获取访问者IP
     * <p/>
     * 在一般情况下使用Request.getRemoteAddr()即可，但是经过nginx等反向代理软件后，这个方法会失效。
     * <p/>
     * 本方法先从Header中获取X-Real-IP，如果不存在再从X-Forwarded-For获得第一个IP(用,分割)，
     * 如果还不存在则调用Request .getRemoteAddr()。
     *
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        ip = request.getHeader("X-Forwarded-For");
        if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值，第一个为真实IP。
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        } else {
            return request.getRemoteAddr();
        }
    }

    /**
     * 获得请求的session id，但是HttpServletRequest#getRequestedSessionId()方法有一些问题。
     * 当存在部署路径的时候，会获取到根路径下的jsessionid。
     *
     * @param request
     * @return
     * @see HttpServletRequest#getRequestedSessionId()
     */
    public static String getRequestedSessionId(HttpServletRequest request) {
        String sid = request.getRequestedSessionId();
        String ctx = request.getContextPath();
        // 如果session id是从url中获取，或者部署路径为空，那么是在正确的。
        if (request.isRequestedSessionIdFromURL() || StringUtils.isBlank(ctx)) {
            return sid;
        } else {
            // 手动从cookie获取
            Cookie cookie = CookieUtils.getCookie(request, Constants.JSESSION_COOKIE);
            if (cookie != null) {
                return cookie.getValue();
            } else {
                return request.getSession().getId();
            }
        }
    }

    public static void main(String[] args) {
    }
}
